package cn.zmwh.common.security.config;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.zmwh.common.core.constants.Constants;
import cn.zmwh.common.core.utils.AuthenticationUtil;
import cn.zmwh.common.security.base.BaseUserDetails;
import cn.zmwh.common.security.base.MutableHttpServletRequest;
import cn.zmwh.common.security.base.UserService;
import cn.zmwh.common.security.util.HttpUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Locale;

/**
 * JWT登录授权过滤器
 *
 * @author: dmzmwh 、
 * @time: 2018-03-07 12:20
 */
@Slf4j
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Autowired
    private UserService userService;

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain) throws ServletException, IOException {


        PathMatcher pathMatcher = new AntPathMatcher();
        String authHeader = request.getHeader(Constants.TOKEN_HEADER);
        String lang = request.getHeader(Constants.LANG_HEADER);
        if(StrUtil.isNotBlank(lang)){
            // 直接设置到Spring的LocaleContextHolder
            LocaleContextHolder.setLocale(Locale.forLanguageTag(lang));
        }
        if (!pathMatcher.match("/api/**", request.getRequestURI()) || StrUtil.isBlank(authHeader)) {
            if (StrUtil.isNotBlank(authHeader) && pathMatcher.match("/*/api/**", request.getRequestURI())) {
                //不强制要求登录，若存在token则解析
                HttpServletRequest requestWrapper = buildParameter(request, response);
                if (requestWrapper != null) {
                    chain.doFilter(requestWrapper, response);
                    return;
                }
            }
            //白名单请求直接放行
            chain.doFilter(request, response);
            return;
        }

        HttpServletRequest requestWrapper = buildParameter(request, response);
        if (requestWrapper != null) {
            chain.doFilter(requestWrapper, response);
            return;
        }
        chain.doFilter(request, response);
    }

    /**
     * 构建参数
     *
     * @param request
     * @param response
     * @return
     * @throws ServletException
     * @throws IOException
     */
    private HttpServletRequest buildParameter(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String tokenPrefix = Constants.TOKEN_PREFIX;
        String authHeader = request.getHeader(Constants.TOKEN_HEADER);
        String authToken = authHeader.substring(tokenPrefix.length());// The part after "Bearer "
        BaseUserDetails userDetail = jwtTokenUtil.getDpUserDetail(authToken);
        String username = userDetail.getUsername();
        Long uid = userDetail.getId();
        String id = uid + "";
        boolean staus = userService.userStatus(uid);//防止客户登录后用户状态变更
        if (!staus) {
            AuthenticationUtil.responseWriter(response);
            return request;
        }
        log.info("checking username:{}", username);

        String terminal = userDetail.getTerminal();
        String nike = userDetail.getNike();
        String method = request.getMethod();
        MutableHttpServletRequest requestWrapper = new MutableHttpServletRequest(request);
        requestWrapper.putHeader(Constants.UID, id);
        requestWrapper.putHeader(Constants.TERMINAL, terminal);
        requestWrapper.putHeader(Constants.TOKEN_NIKE, nike);
        requestWrapper.putHeader(Constants.USERNAME, username);
        String contentType = request.getContentType();

        if (StrUtil.isNotBlank(contentType)) {
            if (contentType.contains("application/json")) {
                String bodyStr = HttpUtil.getPostData(request);
                if (StrUtil.isBlank(bodyStr) || JSONUtil.isTypeJSONObject(bodyStr)) {
                    JSONObject entries = null;
                    if (StrUtil.isBlank(bodyStr)) {
                        entries = new JSONObject();
                    } else {
                        entries = JSONUtil.parseObj(bodyStr);
                    }
                    entries.putOpt(Constants.UID, id);
                    entries.putOpt(Constants.TERMINAL, terminal);
                    entries.putOpt(Constants.TOKEN_NIKE, nike);
                    entries.putOpt(Constants.USERNAME, username);
                    bodyStr = JSONUtil.toJsonStr(entries);
                }
                requestWrapper.setBodyJsonStr(bodyStr);
            }
            if ("GET".equals(method) || contentType.contains("multipart/form-data") || contentType.contains("application/x-www-form-urlencoded")) {
                requestWrapper.addParameter(Constants.TOKEN_NIKE, nike);
                requestWrapper.addParameter(Constants.TERMINAL, terminal);
                requestWrapper.addParameter(Constants.UID, id);
                requestWrapper.addParameter(Constants.USERNAME, username);
            }
        }

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {//不使用上下文可以不封装
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetail, null, userDetail.getAuthorities());
            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }
        return requestWrapper;
    }


}
